練習使用 Geolocation API 取得目前的位置,然後用經緯度去找最近的觀測站。
這邊我用了氣象資料開放平台的 open data。
https://opendata.cwb.gov.tw/dataset/observation/O-A0001-001
首先可能要去氣象資料開放平台取得授權碼,蠻簡單的,登入之後會員資訊有個 API 授權頁面。
然後就可以再他們的 Swagger 中測試。
https://opendata.cwb.gov.tw/dist/opendata-swagger.html#/%E8%A7%80%E6%B8%AC/get_v1_rest_datastore_O_A0001_001
開始寫我們的 API ,老實說我覺得他們的 Model 有點麻煩很多層。
這是我最後要吐給行動端最終的 Model
public class WeatherModel
{
/// <summary>
/// 緯度 (座標系統採TWD67),單位 度
/// </summary>
public double Lat { get; set; }
/// <summary>
/// 經度 (座標系統採TWD67),單位 度
/// </summary>
public double Lon { get; set; }
/// <summary>
/// 測站名稱
/// </summary>
public string LocationName { get; set; }
/// <summary>
/// 測站ID
/// </summary>
public string StationId { get; set; }
/// <summary>
/// 觀測資料時間
/// </summary>
public DateTime ObsTime { get; set; }
/// <summary>
/// ELE 高度,單位 公尺
/// </summary>
public double ELEV { get; set; }
/// <summary>
/// WDIR 風向,單位 度,一般風向 0 表示無風
/// </summary>
public double WDIR { get; set; }
/// <summary>
/// WDSD 風速,單位 公尺/秒
/// </summary>
public double WDSD { get; set; }
/// <summary>
/// TEMP 溫度,單位 攝氏
/// </summary>
public double TEMP { get; set; }
/// <summary>
/// HUMD 相對濕度,單位 百分比率,此處以實數 0-1.0 記錄
/// </summary>
public double HUMD { get; set; }
/// <summary>
/// PRES 測站氣壓,單位 百帕
/// </summary>
public double PRES { get; set; }
/// <summary>
/// H_24R 日累積雨量,單位 毫米
/// </summary>
public double H_24R { get; set; }
/// <summary>
/// H_FX 小時最大陣風風速,單位 公尺/秒
/// </summary>
public double H_FX { get; set; }
/// <summary>
/// H_XD 小時最大陣風風向,單位 度
/// </summary>
public object H_XD { get; set; }
/// <summary>
/// H_FXT 小時最大陣風時間,yyyy-MM-ddThh:mm:ss+08:00
/// </summary>
public object H_FXT { get; set; }
/// <summary>
/// D_TX 本日最高溫,單位 攝氏
/// </summary>
public double D_TX { get; set; }
/// <summary>
/// D_TXT 本日最高溫發生時間,hhmm (小時分鐘)
/// </summary>
public object D_TXT { get; set; }
/// <summary>
/// D_TN 本日最低溫,單位 攝氏
/// </summary>
public double D_TN { get; set; }
/// <summary>
/// D_TNT 本日最低溫發生時間,hhmm (小時分鐘)
/// </summary>
public object D_TNT { get; set; }
/// <summary>
/// CITY 縣市
/// </summary>
public string CITY { get; set; }
/// <summary>
/// CITY_SN 縣市編號
/// </summary>
public string CITY_SN { get; set; }
/// <summary>
/// TOWN 鄉鎮
/// </summary>
public string TOWN { get; set; }
/// <summary>
/// TOWN_SN 鄉鎮編號
/// </summary>
public string TOWN_SN { get; set; }
}
以下都是為了 json to C# 用的
public class WeatherDataModel
{
public bool success { get; set; }
public WeatherDataResult result { get; set; }
public WeatherDataRecords records { get; set; }
}
public class WeatherDataResult
{
public string resource_id { get; set; }
public List<WeatherDataResultFields> fields { get; set; }
}
public class WeatherDataResultFields
{
public string id { get; set; }
public string type { get; set; }
}
public class WeatherDataRecords
{
public List<WeatherDataRecordLocation> location { get; set; }
}
public class WeatherDataRecordLocation
{
public double lat { get; set; }
public double lon { get; set; }
public string locationName { get; set; }
public string stationId { get; set; }
public WeatherDataRecordLocationTime time { get; set; }
public List<WeatherDataRecordLocationWeatherelement> weatherElement { get; set; }
public List<WeatherDataRecordLocationParameter> parameter { get; set; }
}
public class WeatherDataRecordLocationTime
{
public DateTime obsTime { get; set; }
}
public class WeatherDataRecordLocationWeatherelement
{
public string elementName { get; set; }
public object elementValue { get; set; }
}
public class WeatherDataRecordLocationParameter
{
public string parameterName { get; set; }
public string parameterValue { get; set; }
}
API
[HttpGet]
[Route("Weather")]
public async Task<IHttpActionResult> GetWeatherStepAsync(double lat, double lon)
{
var key = System.Configuration.ConfigurationManager.AppSettings["WeatherKey"];
var url = $"https://opendata.cwb.gov.tw/api/v1/rest/datastore/O-A0001-001?Authorization={ key }";
HttpClient httpClient = new HttpClient();
HttpResponseMessage response = await httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
string body = await response.Content.ReadAsStringAsync();
WeatherDataModel data = JsonConvert.DeserializeObject<WeatherDataModel>(body);
var result = data.records.location
.OrderBy(l => Math.Pow(lat - l.lat, 2) + Math.Pow(lon - l.lon, 2))
.FirstOrDefault();
var obj = new WeatherModel();
obj.Lat = result.lat;
obj.Lon = result.lon;
obj.LocationName = result.locationName;
obj.StationId = result.stationId;
obj.ObsTime = result.time.obsTime;
obj.ELEV = Convert.ToDouble(result.weatherElement.FirstOrDefault(e => e.elementName == "ELEV").elementValue);
obj.WDIR = Convert.ToDouble(result.weatherElement.FirstOrDefault(e => e.elementName == "WDIR").elementValue);
obj.WDSD = Convert.ToDouble(result.weatherElement.FirstOrDefault(e => e.elementName == "WDSD").elementValue);
obj.TEMP = Convert.ToDouble(result.weatherElement.FirstOrDefault(e => e.elementName == "TEMP").elementValue);
obj.HUMD = Convert.ToDouble(result.weatherElement.FirstOrDefault(e => e.elementName == "HUMD").elementValue);
obj.PRES = Convert.ToDouble(result.weatherElement.FirstOrDefault(e => e.elementName == "PRES").elementValue);
obj.H_24R = Convert.ToDouble(result.weatherElement.FirstOrDefault(e => e.elementName == "H_24R").elementValue);
obj.H_FX = Convert.ToDouble(result.weatherElement.FirstOrDefault(e => e.elementName == "H_FX").elementValue);
obj.H_XD = result.weatherElement.FirstOrDefault(e => e.elementName == "H_XD").elementValue;
obj.H_FXT = result.weatherElement.FirstOrDefault(e => e.elementName == "H_FXT").elementValue;
obj.D_TX = Convert.ToDouble(result.weatherElement.FirstOrDefault(e => e.elementName == "D_TX").elementValue);
obj.D_TXT = result.weatherElement.FirstOrDefault(e => e.elementName == "D_TXT").elementValue;
obj.D_TN = Convert.ToDouble(result.weatherElement.FirstOrDefault(e => e.elementName == "D_TN").elementValue);
obj.D_TNT = result.weatherElement.FirstOrDefault(e => e.elementName == "D_TNT").elementValue;
obj.CITY = result.parameter.FirstOrDefault(p => p.parameterName == "CITY").parameterValue;
obj.CITY_SN = result.parameter.FirstOrDefault(p => p.parameterName == "CITY_SN").parameterValue;
obj.TOWN = result.parameter.FirstOrDefault(p => p.parameterName == "TOWN").parameterValue;
obj.TOWN_SN = result.parameter.FirstOrDefault(p => p.parameterName == "TOWN_SN").parameterValue;
return Ok(obj);
}
postman 測試一下 API
用來接 API 資料的 Model
weather-model.ts
export class WeatherModel {
Lat: number;
Lon: number;
LocationName: string;
StationId: string;
ObsTime: Date;
ELEV: number;
WDIR: number;
WDSD: number;
TEMP: number;
HUMD: number;
PRES: number;
H_24R: number;
H_FX: number;
H_XD: any;
H_FXT: any;
D_TX: number;
D_TXT: any;
D_TN: number;
D_TNT: any;
CITY: string;
CITY_SN: string;
TOWN: string;
TOWN_SN: string;
}
weather.page.html
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-back-button defaultHref="/"></ion-back-button>
</ion-buttons>
<ion-title>weather</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-grid>
<ion-row>
<ion-col>
最近觀測站 - 天氣資訊
</ion-col>
</ion-row>
<ion-row>
<ion-col>觀測站緯度</ion-col>
<ion-col>
{{ model?.Lat }}
</ion-col>
</ion-row>
<ion-row>
<ion-col>觀測站經度</ion-col>
<ion-col> {{ model?.Lon }}</ion-col>
</ion-row>
<ion-row>
<ion-col>測站ID</ion-col>
<ion-col> {{ model?.StationId }}</ion-col>
</ion-row>
<ion-row>
<ion-col>觀測資料時間</ion-col>
<ion-col> {{ model?.ObsTime }}</ion-col>
</ion-row>
<ion-row>
<ion-col>高度</ion-col>
<ion-col> {{ model?.ELEV }}</ion-col>
</ion-row>
<ion-row>
<ion-col>風向</ion-col>
<ion-col> {{ model?.WDIR }}</ion-col>
</ion-row>
<ion-row>
<ion-col>風速</ion-col>
<ion-col> {{ model?.WDSD }}</ion-col>
</ion-row>
<ion-row>
<ion-col>溫度</ion-col>
<ion-col> {{ model?.TEMP }}</ion-col>
</ion-row>
<ion-row>
<ion-col>相對濕度</ion-col>
<ion-col> {{ model?.HUMD }}</ion-col>
</ion-row>
<ion-row>
<ion-col>測站氣壓</ion-col>
<ion-col> {{ model?.PRES }}</ion-col>
</ion-row>
<ion-row>
<ion-col>日累積雨量</ion-col>
<ion-col> {{ model?.H_24R }}</ion-col>
</ion-row>
<ion-row>
<ion-col>小時最大陣風風速</ion-col>
<ion-col> {{ model?.H_FX }}</ion-col>
</ion-row>
<ion-row>
<ion-col>小時最大陣風風向</ion-col>
<ion-col> {{ model?.H_XD }}</ion-col>
</ion-row>
<ion-row>
<ion-col>小時最大陣風時間</ion-col>
<ion-col> {{ model?.H_FXT }}</ion-col>
</ion-row>
<ion-row>
<ion-col>本日最高溫</ion-col>
<ion-col> {{ model?.D_TX }}</ion-col>
</ion-row>
<ion-row>
<ion-col>本日最高溫發生時間</ion-col>
<ion-col> {{ model?.D_TXT }}</ion-col>
</ion-row>
<ion-row>
<ion-col>本日最低溫</ion-col>
<ion-col> {{ model?.D_TN }}</ion-col>
</ion-row>
<ion-row>
<ion-col>本日最低溫發生時間</ion-col>
<ion-col> {{ model?.D_TNT }}</ion-col>
</ion-row>
<ion-row>
<ion-col>縣市</ion-col>
<ion-col> {{ model?.CITY }}</ion-col>
</ion-row>
<ion-row>
<ion-col>縣市編號</ion-col>
<ion-col> {{ model?.CITY_SN }}</ion-col>
</ion-row>
<ion-row>
<ion-col>鄉鎮</ion-col>
<ion-col> {{ model?.TOWN }}</ion-col>
</ion-row>
<ion-row>
<ion-col>鄉鎮編號</ion-col>
<ion-col> {{ model?.TOWN_SN }}</ion-col>
</ion-row>
</ion-grid>
</ion-content>
weather.page.ts
import { HttpClient } from '@angular/common/http';
import { WeatherModel } from './weather-model';
import { Component, OnInit } from '@angular/core';
import { Plugins } from '@capacitor/core';
const { Geolocation } = Plugins;
@Component({
selector: 'app-weather',
templateUrl: './weather.page.html',
styleUrls: ['./weather.page.scss'],
})
export class WeatherPage implements OnInit {
public model: WeatherModel;
constructor(private http: HttpClient) {}
ngOnInit() {
Geolocation.getCurrentPosition().then((coordinates) => {
this.weather(coordinates.coords.latitude, coordinates.coords.longitude);
});
}
weather(lat: number, lon: number) {
this.http
.get<WeatherModel>('http://192.168.1.102/BES/api/Demo3/Weather', {
params: {
lat: lat.toString(),
lon: lon.toString(),
},
})
.subscribe((result) => {
this.model = result;
});
}
}
最主要就是 Capacitor Geolocation API 的部分
小結語
這篇最麻煩的其實是後端。XDDD
結果